package MapRelated;

import org.osmdroid.DefaultResourceProxyImpl;
import org.osmdroid.ResourceProxy;
import org.osmdroid.tileprovider.MapTile;
import org.osmdroid.tileprovider.MapTileProviderBase;
import org.osmdroid.util.MyMath;
import org.osmdroid.views.util.Mercator;

import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.Point;
import android.graphics.Rect;
import android.graphics.drawable.Drawable;
import android.util.Log;

import com.google.android.maps.GeoPoint;
import com.google.android.maps.MapView;
import com.google.android.maps.Overlay;
import com.google.android.maps.Projection;

/**
 * This class represents an OSM Tiles Overlay at a Google Map. It is a copy from
 * the class org.osmdroid.views.overlay.TilesOverlay (r789) (formerly known as
 * org.andnav.osm.views.overlay.OpenStreetMapTilesOverlay) and modified to make
 * it working.
 * 
 */
@SuppressWarnings("deprecation")
public class OSMOverlay extends Overlay 
{
	private String tag = new String("OSMOverlay");
	private static final boolean DEBUGMODE = false;

	/** Current tile source */
	protected final MapTileProviderBase mTileProvider;

	/* to avoid allocations during draw */
	protected final Paint mPaint = new Paint();
	private final Rect mTileRect = new Rect();
	private final Point mTilePos = new Point();

	public OSMOverlay(final MapTileProviderBase aTileProvider, final Context aContext) 
	{
		this(aTileProvider, new DefaultResourceProxyImpl(aContext));
	}

	public OSMOverlay(final MapTileProviderBase aTileProvider, final ResourceProxy pResourceProxy) {
		// Original line in org.osmdroid.views.overlay.TilesOverlay.java
		// super(pResourceProxy);
		if (aTileProvider == null) 
		{
			throw new IllegalArgumentException("You must pass a valid tile provider to the tiles overlay.");
		}

		this.mTileProvider = aTileProvider;
	}

	public void setAlpha(final int a) {
		this.mPaint.setAlpha(a);
	}

	public int getMinimumZoomLevel() {
		return mTileProvider.getMinimumZoomLevel();
	}

	public int getMaximumZoomLevel() {
		return mTileProvider.getMaximumZoomLevel();
	}

	/**
	 * Whether to use the network connection if it's available.
	 */
	public boolean useDataConnection() {
		return mTileProvider.useDataConnection();
	}

	/**
	 * Set whether to use the network connection if it's available.
	 * 
	 * @param aMode
	 *            if true use the network connection if it's available. if false
	 *            don't use the network connection even if it's available.
	 */
	public void setUseDataConnection(final boolean aMode) {
		mTileProvider.setUseDataConnection(aMode);
	}

	@Override
	public void draw(final Canvas c, final MapView osmv, final boolean shadow) 
	{
		if (DEBUGMODE) {
			Log.v(tag , "draw");
		}

		// Calculate the half-world size
		final Projection pj = osmv.getProjection();
		final int zoomLevel = osmv.getZoomLevel() - 1;
		final int tileSizePx = this.mTileProvider.getTileSource().getTileSizePixels();

		if(zoomLevel >= 8)
		{
			/*
			 * Calculate the amount of tiles needed for each side around the center
			 * one.
			 */
			GeoPoint mapCenter = osmv.getMapCenter();
			Point centerXY = Mercator.projectGeoPoint(mapCenter.getLatitudeE6() / 1E6, 
					mapCenter.getLongitudeE6() / 1E6, zoomLevel, new Point(0, 0));
			final int tileNeededToLeftOfCenter = centerXY.x - 1;
			final int tileNeededToRightOfCenter = centerXY.x + 1;
			final int tileNeededToTopOfCenter = centerXY.y - 1;
			final int tileNeededToBottomOfCenter = centerXY.y + 1;
			final int mapTileUpperBound = 1 << zoomLevel;
			// make sure the cache is big enough for all the tiles
			final int numNeeded = (tileNeededToBottomOfCenter - tileNeededToTopOfCenter + 1)
			* (tileNeededToRightOfCenter - tileNeededToLeftOfCenter + 1);
			mTileProvider.ensureCapacity(numNeeded);
			/* Draw all the MapTiles (from the upper left to the lower right). */
			for (int y = tileNeededToTopOfCenter; y <= tileNeededToBottomOfCenter; y++) 
			{
				for (int x = tileNeededToLeftOfCenter; x <= tileNeededToRightOfCenter; x++) 
				{
					// Construct a MapTile to request from the tile provider.
					final int tileY = MyMath.mod(y, mapTileUpperBound);
					final int tileX = MyMath.mod(x, mapTileUpperBound);
					final MapTile tile = new MapTile(zoomLevel, tileX, tileY);
					Drawable currentMapTile = mTileProvider.getMapTile(tile);
					if (currentMapTile != null) 
					{
						GeoPoint gp = new GeoPoint((int) (Mercator.tile2lat(y, zoomLevel) * 1E6), (int) (Mercator.tile2lon(
								x, zoomLevel) * 1E6));
						pj.toPixels(gp, mTilePos);
						mTileRect.set(mTilePos.x, mTilePos.y, mTilePos.x + tileSizePx, mTilePos.y + tileSizePx);
						currentMapTile.setBounds(mTileRect);
						currentMapTile.draw(c);
					}

					if (DEBUGMODE) 
					{
						c.drawText(tile.toString(), mTileRect.left + 1, mTileRect.top + mPaint.getTextSize(), mPaint);
						c.drawLine(mTileRect.left, mTileRect.top, mTileRect.right, mTileRect.top, mPaint);
						c.drawLine(mTileRect.left, mTileRect.top, mTileRect.left, mTileRect.bottom, mPaint);
					}
				}
			}

			// draw a cross at center in debug mode
			if (DEBUGMODE) 
			{
				final Point centerPoint = pj.toPixels(mapCenter, null);
				c.drawLine(centerPoint.x, centerPoint.y - 9, centerPoint.x, centerPoint.y + 9, mPaint);
				c.drawLine(centerPoint.x - 9, centerPoint.y, centerPoint.x + 9, centerPoint.y, mPaint);
			}
		}
	}
}